﻿/*===============================================================================================
 *  EECS 395: nu-Portal                                                                         *
 *  Programmed by Jeremy Lee                                                                    *
 *  A simple 3D game with a warping mechanic                                                    *
 *  All Models found from turbosquid.com for free                                               *
 *  Camera system based off of the tutorial at                                                  *
 *  http://www.3dgameprogramming.net/2007/07/31/getting-started-with-xna-first-person-camera/   *
 *  ============================================================================================*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace nu_portal
{
    public class Obj3D
    {
        public Model model;
        public Vector3 pos;
        public Vector3 angle;
        public Vector3 vel;
        public Vector3 offset;
        public Vector3 size;
        public bool inAir;

        Matrix[] transforms;

        public Obj3D()
        {
            model = null;
            transforms = null;
            pos = angle = vel = offset = size = Vector3.Zero;
            inAir = false;
        }

        public Obj3D(Model m)
        {
            model = m;
            transforms = new Matrix[model.Bones.Count];
            pos = angle = vel = Vector3.Zero;
            offset = m.Meshes[0].BoundingSphere.Center; // Probably wrong
            size = new Vector3(m.Meshes[0].BoundingSphere.Radius);
            inAir = false;
        }

        public Obj3D(Model m, Vector3 p, Vector3 a, Vector3 v, Vector3 o, Vector3 s)
        {
            model = m;
            transforms = new Matrix[model.Bones.Count];
            pos = p;
            angle = a;
            vel = v;
            offset = o;
            size = s;
            inAir = false;
        }

        public virtual void update(float elapsedTime)
        {
            if (pos.Y < 0)
            {
                inAir = false;
                pos.Y = 0;
            }
            if (inAir)
                vel.Y -= 0.049f;
            else
                vel.Y = 0;
            pos += vel * elapsedTime;
        }

        public void draw( Matrix view, Matrix project )
        {
            model.CopyAbsoluteBoneTransformsTo( transforms );
            foreach( ModelMesh mm in model.Meshes ){
                foreach( BasicEffect effect in mm.Effects )
                {
                    effect.World = transforms[mm.ParentBone.Index] * Matrix.CreateRotationZ(angle.Z) *
                                   Matrix.CreateRotationY(angle.Y) * Matrix.CreateRotationX(angle.X) *
                                   Matrix.CreateTranslation(pos);
                    effect.View = view;
                    effect.Projection = project;
                    effect.EnableDefaultLighting();
                }
                mm.Draw();
            }
        }

        public virtual bool collides(Obj3D that)
        {
            return this.makeBox().Intersects(that.makeBox());
        }

        public virtual Vector3 collidePlane(Obj3D that)
        {
            
            
            BoundingBox thisBox = makeBox();
            BoundingBox thatBox = that.makeBox();
            Vector3 res = Vector3.Zero;
            /*
            if ((vel.X > 0 || that.vel.X < 0) && pos.X < that.pos.X)
                res.X = 1;
            if ((vel.X < 0 || that.vel.X > 0) && pos.X > that.pos.X)
                res.X = -1;
            if ((vel.Y > 0 || that.vel.Y < 0) && pos.Y < that.pos.Y)
                res.Y = 1;
            if ((vel.Y < 0 || that.vel.Y > 0) && pos.Y > that.pos.Y)
                res.Y = -1;
            if ((vel.Z > 0 || that.vel.Z < 0) && pos.Z < that.pos.X)
                res.Z = 1;
            if ((vel.Z < 0 || that.vel.Z > 0) && pos.Z > that.pos.Z)
                res.Z = -1;

            if (res.X != 0)
            {
                vel.X = 0;
                that.vel.X = 0;
            }
            if (res.Y != 0)
            {
                vel.Y = 0;
                that.vel.Y = 0;
            }
            if (res.Z != 0)
            {
                vel.Z = 0;
                that.vel.Z = 0;
            }
            //*/
            /*
            Vector3 dir = (that.pos + that.offset) - (pos + offset);
            if (Math.Abs(dir.X) >= Math.Abs(dir.Y) && Math.Abs(dir.X) >= Math.Abs(dir.Z))
                res.X = 1;
            if (Math.Abs(dir.Y) >= Math.Abs(dir.X) && Math.Abs(dir.Y) >= Math.Abs(dir.Z))
                res.Y = 1;
            if (Math.Abs(dir.Z) >= Math.Abs(dir.Y) && Math.Abs(dir.Z) >= Math.Abs(dir.X))
                res.Z = 1;
            //*/
            /*
            if ((vel.Y >= 0 || that.vel.Y < 0) && pos.Y < that.pos.Y)
                res.Y = 1;
            if ((vel.Y <= 0 || that.vel.Y > 0) && pos.Y > that.pos.Y)
                res.Y = -1;
            if (res.Y != 0)
            {
                vel.Y = 0;
                that.vel.Y = 0;
            }
            //*/
            Vector3 dir = (that.pos + that.offset) - (pos + offset);
            if (Math.Abs(dir.Y) > Math.Abs(dir.X) && Math.Abs(dir.Y) > Math.Abs(dir.Z))
                if (dir.Y > 0)
                    res.Y = 1;
                else
                    res.Y = -1;
            if (Math.Abs(dir.X) > Math.Abs(dir.Y) && Math.Abs(dir.X) > Math.Abs(dir.Z))
                res.X = 1;
            if (Math.Abs(dir.Z) > Math.Abs(dir.Y) && Math.Abs(dir.Z) > Math.Abs(dir.X))
                res.Z = 1;
            //*/
            return res;
        }

        public virtual BoundingBox makeBox()
        {
            return new BoundingBox(pos + offset - size / 2 , pos + offset + size / 2 );
        }
    }

    public class Camera : Obj3D
    {
        public Matrix view;
        public Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 4/3, 1, 10000);

        public Camera() : base()
        {
            createView();
        }

        public Camera(Model m) : base(m)
        {
            createView();
        }

        public Camera(Model m, Vector3 p, Vector3 a, Vector3 v, Vector3 o, Vector3 s) : base(m, p, a, v, o, s)
        {
            createView();
        }

        private void createView()
        {
            view = Matrix.Identity;
            view *= Matrix.CreateTranslation(-(pos + offset));
            view *= Matrix.CreateRotationZ(angle.Z);
            view *= Matrix.CreateRotationY(angle.Y);
            view *= Matrix.CreateRotationX(angle.X);
        }

        public override void update(float elapsedTime)
        {
            createView();
            base.update(elapsedTime);
        }
    }
}
